<%@ page language="java" contentType="text/xml; charset=UTF-8" isELIgnored="false" %><%@ 
include file="/WEB-INF/jsp/include.jsp" 
%><?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs
    title="Flash Cards"
    height="500px"
    description="Dynamic flash cards Google Wave Gadget"
    author="Bryan Bibat"
    author_email="bryan.bibat@gmail.com">
   <Require feature="wave"/>
   <Require feature="dynamic-height"/>
   <Require feature="locked-domain"/>
  </ModulePrefs>
  <Content type="html">
  <![CDATA[
  <script src="http://wave-flashcards.appspot.com/js/jquery-1.3.2.min.js"></script>
  <script src="http://wave-flashcards.appspot.com/js/jquery-ui-1.7.2.custom.min.js"></script>
  <script src="http://wave-flashcards.appspot.com/js/json.js"></script>
  <link rel="stylesheet" href="http://wave-flashcards.appspot.com/css/cupertino/jquery-ui-1.7.2.custom.css" type="text/css" media="all" />
   
  <script type="text/javascript">

  var DATA = <c:out value="${deck.jsonData}" escapeXml="false" />;
  var ROUNDS = <c:out value="${settings.rounds}"/>;
  var CHOICES = <c:out value="${settings.choices}"/>;
  var PENALTY = <c:out value="${settings.penalty}"/>;

  //
  //---------------------------------------------------------- UTILITY FUNCTIONS
  //
  
  // workaround for 404 error in images
  function getThumbnailUrl(participant) {
    if (participant.getThumbnailUrl() == "https://wave.google.com/static/images/unknown.gif") {
      return "https://wave.google.com/wave/static/images/unknown.gif"
    } 
    return participant.getThumbnailUrl();
  }
  
  //In case I change my mind about "game_id" somewhere down the line
  function getGameId(state) {
    return state.get("game_id");
  }
 
  // Remove robots. Derived from connect-four gadget
  function getPlayers() {
    var players = new Array();
    var participants = wave.getParticipants();

    for (var idx = 0; idx < participants.length; idx++) {
      var pid = participants[idx].getId();
      if(/@.*appspot\.com$/.exec(pid) != null ? true : false)
          continue;
      if(/@a\.gwave\.com$/.exec(pid) != null ? true : false)
          continue;
      players.push(participants[idx]);    
    }
    return players;
  }
  
  function getReadableTimeDiff(last_move, start_time) {
    var seconds = Math.floor( (parseInt(last_move) - parseInt(start_time)) /1000)
    var minutes = Math.floor(seconds / 60)
    seconds = Math.floor(seconds % 60)
    
    var hours = Math.floor(minutes / 60)
    minutes = Math.floor(minutes % 60)
    
    var display = "";
    if (hours) {
      display += hours + " hour" + (hours == 1 ? "" : "s") + " "; 
    }
    if (minutes) {
      display += minutes + " minute" + (minutes == 1 ? "" : "s") + " "; 
    }
    if (seconds) {
      display += seconds + "." + (parseInt(last_move) - parseInt(start_time)) % 1000 + " seconds"; 
    }
    return display;
  }
  
  function getPenaltyWeight() {
     var state = wave.getState();
     if (state.get("penalty") == null) {
       return 1;
     } 
     return parseInt(state.get("penalty"));
  }
  
  function randomizeThenGetTopItems(list, count) {

     //TODO double check size of list for count
     
     //use a modified Fisher-Yates shuffle
     var topItems = new Array();
     for (var i = 0; i < count; i++) {
       var other = i + parseInt(Math.random() * (list.length - i));
       var temp = list[i];
       list[i] = list[other];
       list[other] = temp;
       topItems.push(list[i]);
     } 
     return topItems;
  }
  
  //
  //------------------------------------------------------------------ CALLBACKS  
  //
  
  function waveParticipantUpdated() {

    //moved display to updatestate   
    gadgets.window.adjustHeight();
        
  }

  function stateUpdated() {
   
    if (isGameInitialized()) {

      showCurrentRound();
      updatePlayerScores();
       
      if (isGameOver()) {
        showWinners();
      } else {
        hideWinners();
      }
      gadgets.window.adjustHeight();
      
    } 
    
  }

  //
  //---------------------------------------------------- STATE UPDATED FUNCTIONS
  //
  
  function isGameInitialized() {
    return wave.getState().get("game_id");
  }  
  
  //
  // - Display functions
  //
  
  function showCurrentRound() {
    var state = wave.getState();
    var gameId = getGameId(state);
    
    if (isGameOver()) {
      $("#left_card_panel_container").css({display: 'none'});
      $("#right_card_panel_container").css({display: 'none'});
      $("#challenge_panel").text("");
      return;
    } 
    
    var roundId = getRoundId(state, gameId);
    
    var roundState = JSON.parse(state.get("round_" + roundId + "_" + gameId));
    
    $("#challenge_panel").text(roundState.challenge);
    
    var container = (roundId % 2 == 0 ? $("#left_card_panel_container") : $("#right_card_panel_container"));
    container.css({display: 'block'});
    container.children().remove();
    for (var cardNo = 0; cardNo < roundState.choices.length; cardNo++) {
      var card = $("<div class='card'>" + roundState.choices[cardNo] + "</div>");
      
      if (cardNo == roundState.correct) {
        card.attr("id", "correct_" + roundId);
        card.click(function () { 
          var state = wave.getState();
          var gameId = getGameId(state);
          delta = {};
          delta[$(this).attr("id") + "_" + gameId] = wave.getViewer().getId();
          delta["last_move_" + gameId] = wave.getTime();
          state.submitDelta(delta);
        });
      } else {
        card.click(function () { 
          var state = wave.getState();
          var gameId = getGameId(state);
          var mistakes = state.get("mistakes_" + wave.getViewer().getId() + "_" + gameId);
          if (!mistakes) {
            mistakes = 0;
          }
          state.submitValue("mistakes_" + wave.getViewer().getId() + "_" + gameId, parseInt(mistakes) + 1);
        });
      }
      
      container.append(card);
    }
    
    var otherContainer = (roundId % 2 == 1 ? $("#left_card_panel_container") : $("#right_card_panel_container"));
    otherContainer.css({display: 'none'});
        
  } 
  
  function getRoundId(state, gameId) {
    for (var round = 0; true; round++) {
      if (state.get("correct_" + round + "_" + gameId) == null) {
        // first missing correct guess is the round number
        return round;
      }
    }
  }
  
  function updatePlayerScores() {
    
    var scores = calculateScoreMap();
    
    var state = wave.getState();
    var gameId = getGameId(state);
    var players = getPlayers(); 
    
    for (var idx = 0; idx < players.length; idx++) {
      var player = players[idx];
      var playId = player.getId();
      
      var penalty = (state.get("mistakes_" + playId + "_" + gameId) ?  
        parseInt(state.get("mistakes_" + playId + "_" + gameId)) : 0);
      var score = scores[playId] ? scores[playId] : 0;
      var displayScore = score - (penalty * getPenaltyWeight());
      
      
      if (penalty == 0 && score == 0) {
        
        if (document.getElementById(playId) != null) {
          $(document.getElementById(playId)).remove();
        }
         
        continue;
      }
      
      if (document.getElementById(playId)) {
      
        $(document.getElementById("score_" + playId)).text(pointsDisplay(displayScore));
        $(document.getElementById("correct_" + playId)).text(score);
        $(document.getElementById("mistakes_" + playId)).text(penaltyDisplay(penalty));
        
      } else {
      
        var playerDiv = $("<div class='player' id='" + player.getId() + "'></div>");
        
        var str = "<img height='30px' width='30px' src='" + getThumbnailUrl(player) + "'>";            
        if (playId == wave.getViewer().getId())
          str += "me";
        else
          str += player.getDisplayName();
        str += ": <span id='score_" + playId +"'>" + pointsDisplay(displayScore) + "</span> " +
          "(<span id='correct_" + playId +"'>" + score + "</span> correct, " +
          "<span id='mistakes_" + playId +"'>" + penaltyDisplay(penalty) + "</span>)";
        
        playerDiv.html(str);
        
        $("#participants").append(playerDiv);
        
      } 
      
        
    }
    
  }
  
  function pointsDisplay(displayScore) {
    return "" + displayScore + (displayScore == 1 ? " point" : " points");
  }
  
  function penaltyDisplay(penalty) {
    return "" + penalty + (penalty == 1 ? " mistake" : " mistakes");
  }
    
  function calculateScoreMap() {
    var state = wave.getState();
    var gameId = getGameId(state);
    var scores = {}; 
    
    for (var roundId = 0; true; ++roundId) {
      pickerId = state.get("correct_" + roundId + "_" + gameId);
      if (pickerId) {
        if (scores[pickerId]) {
          scores[pickerId] += 1; 
        } else {
          scores[pickerId] = 1;
        }
      } else {
        break;
      }
    }
    
    return scores;
  }
  
  function isGameOver() {
    var state = wave.getState();
    var gameId = getGameId(state);
    var roundId = getRoundId(state, gameId);
    var roundState = state.get("round_" + roundId + "_" + gameId);
    return roundState == null; 
  }   
    
  function showWinners() {
    $('#winners').css({display: 'block'});
    
    var state = wave.getState();
    var gameId = getGameId(state);

    var players = getPlayers();
    var max = null;
    var maxId = new Array();
    
    var scores = calculateScoreMap();
    
    //determine winners
    for (var idx = 0; idx < players.length; idx++) {
      var player = players[idx];
      var playId = player.getId();
     
      var penalty = (state.get("mistakes_" + playId + "_" + gameId) ?  
        parseInt(state.get("mistakes_" + playId + "_" + gameId)) : 0);
      var score = (scores[playId] ? scores[playId] : 0) - (penalty * getPenaltyWeight());
      
      if (max == null) {
        max = score;
        maxId.push(playId);
      } else {
        if (score > max) {
          maxId = new Array();
          maxId.push(playId);
          max = score;
        } else if (score == max) {
          maxId.push(playId);
        }
      } 
      
    }
    
    $('#winners').html(buildWinnerText(max, maxId));
    
  }  
  
  function buildWinnerText(max, maxId) {
  
    var state = wave.getState();
    var gameId = getGameId(state);
    
    var winners = "";
    if (maxId.length == 1) {
      winners += "<img height='30px' width='30px' src='" + getThumbnailUrl(wave.getParticipantById(maxId[0])) + "'>"; 
      if (maxId[0] == wave.getViewer().getId()) {
        winners += "You won with ";
      } else {           
        winners += wave.getParticipantById(maxId[0]).getDisplayName() + " won with ";
      }
      winners += max + " point" + (max == 1 ? "" : "s");
    } else {
      winners = "It's a tie between ";
      for (var j = 0; j < maxId.length; j++) {
        if (j > 0) {
          winners += " and ";
        }
        winners += "<img height='30px' width='30px' src='" + getThumbnailUrl(wave.getParticipantById(maxId[j])) + "'>"; 
        if (maxId[j] == wave.getViewer().getId()) {
          winners += "you";
        } else {           
          winners += wave.getParticipantById(maxId[j]).getDisplayName();
        }
      }
      
    }
    winners += "! (" + getReadableTimeDiff(state.get("last_move_" + gameId), state.get("time_" + gameId)) + ")";
    return winners;
  }
  
  function hideWinners() {
    $('#winners').css({display: 'none'});
  }  

  //
  //------------------------------------------------------------------- NEW GAME
  //
  
  function newGame() {
    var delta = {};
    
    clearState(delta);
    initializeGame(delta);
    
    return false;
  }

  // - Initializing functions  
  function clearState(delta) {
    var state = wave.getState();
    for (var idx = 0; idx < state.getKeys().length; idx++) {
      delta[state.get(state.getKeys()[idx])] = null;      
    }
  }
  
  function initializeGame(delta){
  
    if (delta == null) {
      delta = {};
    }
    
    //prevent unsynched moves from previous games from affecting new games
    var gameId = Math.floor(100000 * Math.random()).toString();
    delta["game_id"] = gameId;
    
    delta["penalty"] = PENALTY;
    
    shuffleCards(delta, gameId);
    setGameStartTime(delta, gameId);

    wave.getState().submitDelta(delta);
    
  }
  
  function shuffleCards(delta, gameId) {
    
    var cardPool = new Array(); 
    
    for (var i = 0; i < DATA.length; i++) {
      cardPool.push(DATA[i]);
    }
    
    
    for (var roundId = 0; roundId < ROUNDS; ++roundId) {
    
      var topItems = randomizeThenGetTopItems(cardPool, CHOICES);
      var challenge = topItems[0][0];
      
      var listToStore = new Array();
      for (var idx = 0; idx < topItems.length; idx++) {
        listToStore.push(topItems[idx][1]);
      }
      
      var correct = parseInt(Math.random() * topItems.length)
      var temp = listToStore[0];
      listToStore[0] = listToStore[correct];
      listToStore[correct] = temp;
      
      var roundState = {"challenge" : challenge, "correct": correct,
        "choices" : listToStore };      

      delta['round_' + roundId + '_' + gameId] = JSON.stringify(roundState);
      
    }
  }
    
  function setGameStartTime(delta, gameId) {
    delta["time" + '_' + gameId] = wave.getTime();
  }
  
  
  // MAIN
 
  function main() {
    if (wave && wave.isInWaveContainer()) {
      wave.setStateCallback(stateUpdated);
      wave.setParticipantCallback(waveParticipantUpdated);

      $('#dialog').dialog({
        autoOpen: false
      });
      $('#dialog_link').click(function(){
        $('#dialog').dialog('open');
        $('#help_penalty').text(getPenaltyWeight());
        $('#help_link').blur();
        return false;
      });
      $('#reset_link').click(newGame);

    }
  };
  
  gadgets.util.registerOnLoadHandler(main);
  </script>

  <style>
  body {
    font-family : Trebuchet MS, Helvetica, sans-serif;
    font-size: 12px;
  }
  
  #title {
    font-size: 24px;
  }
  
  #container {
    border: 1px solid rgb(221, 221, 221); 
    padding: 3px;
  }
  
  #innercontainer {
    width:500px; 
    border: 0px; 
    position: relative; 
    margin: 5px; 
    border: 1px solid rgb(221, 221, 221);
    overflow:auto;
  }
  
  .player {
    padding:1px;
    margin:1px;
  }
  
  a {
    padding: 0.4em 1em 0.4em 1em;
    position: relative;
    text-decoration: none;
  }
  
  #help_link {
    padding: 0; 
    text-decoration: underline; 
    border: none;
  }

  #challenge_panel {
    padding-top: 7px;
    clear:both;
    text-align:center;
    font-weight: bold;
    font-size: 120%;
    word-wrap: break-word;
  }
  
  #left_card_panel {
    width: 250px; 
    float: left;
  }
  
  #right_card_panel {
    width: 250px; 
    float: right;
  }
  
  .card {
    border: 1px solid #101010;
    padding: 3px;
    margin: 3px;
    margin-left:auto;
    margin-right:auto;
    text-align:center;
    cursor: pointer;
    word-wrap: break-word;
  }
  
  </style>
  
  <div id="container">
    <span id="title">Flashcards: <c:out value="${deck.title}"/><span id="game_title"></span></span>
    <p>
      <a href="#" id="dialog_link" class="ui-state-default ui-corner-all">Show Rules</a> 
      <a href="#" id="reset_link" class="ui-state-default ui-corner-all">New Game</a> 
    </p>
    <div id="innercontainer">
      <div id="challenge_panel">&nbsp;</div>
      <div id="left_card_panel">
	      <div id="left_card_panel_container" class="card_container">
	        &nbsp;  
	      </div>        
      </div>
      <div id="right_card_panel">
        <div id="right_card_panel_container" class="card_container">
          &nbsp;
        </div>        
      </div>
    </div>
    <div id="participants"></div>
    <div id="winners" style="display:none"></div>
    <div id="dialog" title="Rules of the Game">
      <p>Click the item that corresponds to the displayed text to earn points. The 
      penalty for clicking a wrong answer is <span id="help_penalty">1</span> point/s.</p>
      <p>The player with the most points wins the game.</p>   
    </div>
  </div>
]]></Content>
</Module>
